package com.guigu.qingcheng.service.impl;

import com.alibaba.fastjson.JSON;
import com.guigu.qingcheng.mapper.TbBrandMapper;
import com.guigu.qingcheng.pojo.TbCategory;
import com.guigu.qingcheng.service.GoodsServiceFeign;
import com.guigu.qingcheng.service.SkuSearchService;
import com.guigu.qingcheng.utils.R;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class SkuSearchServiceImpl implements SkuSearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired(required = false)
    private GoodsServiceFeign goodsServiceFeign;


    @Override
    public Map search(Map<String, String> searchMap) {
        //1.封装查询请求
        SearchRequest searchRequest=new SearchRequest("sku");
        searchRequest.types("doc"); //设置查询的类型
        SearchSourceBuilder searchSourceBuilder=new
                SearchSourceBuilder();
        //布尔查询构建器
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //1.1 关键字搜索
        if(!StringUtils.isEmpty(searchMap.get("keywords"))){
            MatchQueryBuilder matchQueryBuilder=
                    QueryBuilders.matchQuery("name",searchMap.get("keywords"));
            boolQueryBuilder.must(matchQueryBuilder);
        }else {
            MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
            boolQueryBuilder.must(matchAllQueryBuilder);
        }


        searchSourceBuilder.query(boolQueryBuilder);

        //1.2根据商品分类过滤
        if(searchMap.get("category")!=null){
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("categoryName", searchMap.get("category"));
            boolQueryBuilder.filter(termQueryBuilder);
        }

        //1.3品牌过滤
        if(searchMap.get("brand")!=null){
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("brandName", searchMap.get("brand"));
            boolQueryBuilder.filter(termQueryBuilder);
        }

        //1.4规格过滤
        for (String key:searchMap.keySet()){
            if (key.startsWith("spec.")){
                TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery(key+".keyword", searchMap.get(key));
                boolQueryBuilder.filter(termQueryBuilder);
            }
        }

        //1.5价格过滤
        if (searchMap.get("price")!=null){
            String[] prices = searchMap.get("price").split("-");
            if(!prices[0].equals("0")){//最低价格不等于0
                RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price").gte(prices[0] + "00");
                boolQueryBuilder.filter(rangeQueryBuilder);
            }
            if(!prices[1].equals("*")){ //如果价格由上限 
                RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price").lte(prices[1] + "00");
                boolQueryBuilder.filter(rangeQueryBuilder);
            }
        }
        //获取分页参数
        //查询是否有页码
        if (searchMap.get("pageNo")==null)
        {
            searchMap.put("pageNo","1");
        }
        int pageNo = Integer.parseInt(searchMap.get("pageNo"));//页码
        int pageSize = 30;//页大小
        int fromIndex = (pageNo-1)*pageSize;//开始索引

        //分页查询
        searchSourceBuilder.from(fromIndex);//开始索引
        searchSourceBuilder.size(pageSize);//每页记录数设置

        //排序
        String sort = searchMap.get("sort");//排序字段
        String sortOrder = searchMap.get("sortOrder");//排序规则

        if (!"".equals(sort)&&sort!=null)//判断是否综合排序
        {
            searchSourceBuilder.sort(sort, SortOrder.valueOf(sortOrder));
        }

        //高亮设置
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name").preTags("<font style='color:red'>").postTags("</font>");
        searchSourceBuilder.highlighter(highlightBuilder);

        searchSourceBuilder.query(boolQueryBuilder);
        //聚和查询(商品分类)
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("sku_category").field("categoryName");
        searchSourceBuilder.aggregation(termsAggregationBuilder);
        searchRequest.source(searchSourceBuilder);
        //.....

        //2.封装查询结果
        Map resultMap=new HashMap();
        try {
            SearchResponse searchResponse =
                    restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits searchHits = searchResponse.getHits();
            long totalHits = searchHits.getTotalHits();
            resultMap.put("total", totalHits);
            SearchHit[] hits = searchHits.getHits();
            //2.1 商品列表
            List<Map<String,Object>> resultList=new ArrayList<Map<String,Object>>();
            for(SearchHit hit:hits){
                Map<String, Object> skuMap = hit.getSourceAsMap();
                skuMap.put("skuId",hit.getId());
               if (!StringUtils.isEmpty(searchMap.get("keywords"))){
                   //设置name高亮
                   Map<String, HighlightField> highlightField = hit.getHighlightFields();
                   HighlightField name = highlightField.get("name");
                   Text[] fragments = name.fragments();
                   String s = fragments[0].toString();
                   skuMap.put("name",s);//用高亮替换原内容
                   resultList.add(skuMap);
               }else {
                  resultList.add(skuMap);
               }
            }
            resultMap.put("rows",resultList);

            //2.2商品分类列表
            Aggregations aggregations = searchResponse.getAggregations();
            Map<String, Aggregation> aggregationMap = aggregations.getAsMap();
            Terms terms = (Terms) aggregationMap.get("sku_category");
            List<? extends Terms.Bucket> buckets = terms.getBuckets();
            List<String> categoryList=new ArrayList();
            for( Terms.Bucket bucket:buckets ){
                categoryList.add(bucket.getKeyAsString());
            }
            resultMap.put("categoryList",categoryList);

            String categoryName = "";//商品分类名称
            if (searchMap.get("category")==null){//如果没有分类条件
                if (categoryList.size()>0){
                    categoryName=categoryList.get(0);//提取分类列表第一个分类
                }
            }else {
                categoryName = searchMap.get("category");//取出分类参数
            }
            List<Map> brandMap = null;
            List<Map> specMap = null;
            if (categoryName!=""){
                if (redisTemplate.boundValueOps(categoryName).get()==null)
                    queryByBrandSpec(categoryName);
                Map map = (Map) redisTemplate.opsForValue().get(categoryName);
                if (map!=null){
                    List list = (List) map.get(categoryName);
                    brandMap = (List<Map>) list.get(0);
                    specMap = (List<Map>) list.get(1);
                }
            }

            //2.3品牌列表
            if (searchMap.get("brand")==null){
                resultMap.put("brandList",brandMap);
            }

            //2.4规格列表
            if (categoryName!=""){
                for (Map spec:specMap) {
                    String[] options = ((String)spec.get("options")).split(",");//规格选项列表
                    spec.put("options",options);
                }
            }
            resultMap.put("specList",specMap);

            //2.5页码
            long totalCount= searchHits.getTotalHits();//总记录数
            long pageCount = (totalCount%pageSize==0)?(totalCount/pageSize):(totalCount/pageSize+1);//总页数
            resultMap.put("totalPages",pageCount);


        } catch (IOException e) {
            e.printStackTrace();
        }
        return resultMap;
    }

    //方式一设置定时任务查询
    @Scheduled(cron = "0 0 1 * * ?")
    private void queryBrandSpec(){
        R category = goodsServiceFeign.list();
        Map<String, Object> data = category.getData();
        List<TbCategory> tbCategories = JSON.parseArray(JSON.toJSONString(data.get("list")), TbCategory.class);
        for (TbCategory tbcategory:tbCategories) {
            R brand = goodsServiceFeign.queryByCategoryName(tbcategory.getName());
            Map<String, Object> brandData = brand.getData();
            List<Map> brandList = (List<Map>) brandData.get("brandList");//根据分类查询出brandlist
            R spec = goodsServiceFeign.queryListByCategoryName(tbcategory.getName());
            Map<String, Object> specData = spec.getData();
            List<Map> specList = (List<Map>) specData.get("specList");//根据分类查询出speclist
            Map map = new HashMap();//保存分类名称 -brand和spec
            List list = new ArrayList();
            list.add(brandList);
            list.add(specList);
            map.put(tbcategory.getName(),list);
            redisTemplate.opsForValue().set(tbcategory.getName(),map);
        }
    };

    //方式二设置随机删除策略
    private void queryByCategory(){
        R category = goodsServiceFeign.list();
        Map<String, Object> data = category.getData();
        List<TbCategory> tbCategories = JSON.parseArray(JSON.toJSONString(data.get("list")), TbCategory.class);
        for (TbCategory tbcategory:tbCategories) {
            R brand = goodsServiceFeign.queryByCategoryName(tbcategory.getName());
            Map<String, Object> brandData = brand.getData();
            List<Map> brandList = (List<Map>) brandData.get("brandList");//根据分类查询出brandlist
            R spec = goodsServiceFeign.queryListByCategoryName(tbcategory.getName());
            Map<String, Object> specData = spec.getData();
            List<Map> specList = (List<Map>) specData.get("specList");//根据分类查询出speclist
            Map map = new HashMap();//保存分类名称 -brand和spec
            List list = new ArrayList();
            list.add(brandList);
            list.add(specList);
            map.put(tbcategory.getName(),list);
            Random random = new Random();
            int i = random.nextInt(30)+30;
            redisTemplate.opsForValue().set(tbcategory.getName(),map,i, TimeUnit.MINUTES);
        }
    }

    private void queryByBrandSpec(String categoryName){
        R brand = goodsServiceFeign.queryByCategoryName(categoryName);
        Map<String, Object> brandData = brand.getData();
        List<Map> brandList = (List<Map>) brandData.get("brandList");//根据分类查询出brandlist
        R spec = goodsServiceFeign.queryListByCategoryName(categoryName);
        Map<String, Object> specData = spec.getData();
        List<Map> specList = (List<Map>) specData.get("specList");//根据分类查询出speclist
        Map map = new HashMap();//保存分类名称 -brand和spec
        List list = new ArrayList();
        list.add(brandList);
        list.add(specList);
        map.put(categoryName,list);
        redisTemplate.opsForValue().set(categoryName,map);
    };

}
